package dbmanager

import (
	"database/sql/driver"
	"fmt"
	"reflect"
	"strings"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gitee.com/night-tc/gobige/redislib"
)

/*
RedisUtilDBData.go 文件提供了对 Redis 数据库的操作封装，主要用于管理玩家在 Redis 中的数据。
该文件包含了对玩家数据的增删改查操作，支持批量操作和单行操作。
*/

// group里服务器使用keyname进行操作的方法，会按PID进行分组
// GroupPlayer:123
func getGroupPlayer(pid uint64, tname string) string {
	if tname == "" {
		return fmt.Sprint("P:", pid)
	} else {
		return fmt.Sprint("P:", pid, ":", tname)
	}
}

// DataUtil 用于管理玩家在Redis中的数据操作
type DataUtil struct {
	pid uint64 // 玩家ID
	//获取redis操作的方法
	getRedisF func() *redislib.RedisHandleModel
}

// NewRedisUtilDBData 创建一个新的DataUtil实例，用于管理指定玩家的Redis数据
func NewRedisUtilDBData(pid uint64) (result *DataUtil) {
	result = new(DataUtil)
	result.pid = pid
	result.getRedisF = global.GetRedisAccess(global.RedisName_Data).GetConn

	return
}

// Key 获取Redis中的Key，根据玩家ID和表名生成
func (utilmd *DataUtil) Key(tname string) string {
	return getGroupPlayer(utilmd.pid, tname)
}

// SetDatas 批量设置行数据，将多个DBDataModel对象的数据批量写入Redis
func (utilmd *DataUtil) SetDatas(pmsgs []*protomsg.DBDataModel) error {
	rd := utilmd.getRedisF()
	argsli := make(map[string]redislib.Args)
	for _, v := range pmsgs {
		tname, uid := GetKeyNameResolve(v.KeyName)
		if args, ok := argsli[tname]; ok {
			args[common.NewStringAny(uid).ToString()] = v.MsgContent
			argsli[tname] = args
		} else {
			args = make(redislib.Args)
			args[common.NewStringAny(uid).ToString()] = v.MsgContent
			argsli[tname] = args
		}
	}
	for tname, args := range argsli {
		if _, err := rd.HMSet(rd.Ctx, utilmd.Key(tname), args).Result(); err != nil {
			return err
		}

	}
	return nil

}

// DelDatas 删除指定表的一些行，根据表名和键名列表删除Redis中的数据
func (utilmd *DataUtil) DelDatas(tname string, keynames []string) {
	if len(keynames) == 0 {
		return
	}
	rd := utilmd.getRedisF()
	rd.HDel(rd.Ctx, utilmd.Key(tname), keynames...)
}

// DelDataArray 删除标记为需要删的数据行，根据DBDataModel列表删除Redis中的数据
func (utilmd *DataUtil) DelDataArray(mli []*protomsg.DBDataModel) {
	if len(mli) == 0 {
		return
	}
	kmapli := make(map[string][]string)
	for _, vmd := range mli {
		tname, uid := GetKeyNameResolve(vmd.KeyName)
		if kli, ok := kmapli[tname]; ok {
			kli = append(kli, common.NewStringAny(uid).ToString())
			kmapli[tname] = kli
		} else {
			kli = make([]string, 1, 10)
			kli[0] = common.NewStringAny(uid).ToString()
			kmapli[tname] = kli
		}
	}
	for tname, keys := range kmapli {
		utilmd.DelDatas(tname, keys)
	}
}

//-----------------------------

/*
多行数据:一个用户有多行，同时支持标脏字段的操作方法
以下的方法是针对一个用户多行数据的，每个字段都需要单独更新的时候，才使用的的逻辑
*/

// RowsSingleGet 获取指定表的所有行数据，返回一个键值对的映射
func (utilmd *DataUtil) RowsSingleGet(table string) (result map[string][]byte, err error) {
	rd := utilmd.getRedisF()
	result, err = rd.HgetallByBytesMap(utilmd.Key(table))
	return
}

// RowsSingleSet 写入多行数据，将指定表的数据写入Redis
func (utilmd *DataUtil) RowsSingleSet(table string, uid uint64, li map[string]reflect.Value) (err error) {
	rd := utilmd.getRedisF()
	args := make(map[string]interface{})
	for modname, v := range li {
		defOpt := func(val interface{}) {
			if uid == 0 {
				args[modname] = val
			} else {
				args[utilmd.GetFieldKey(uid, modname)] = val
			}
		}
		switch v.Kind() {
		case reflect.Ptr:
			if v.IsNil() {
				//指针类型
				//NOTE: redis 存储 nil=="" 字符串,没有"nil" 反序列化还是会有值得.
				if uid == 0 {
					args[modname] = ""
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = ""
				}
				break
			} else if val, ok := v.Elem().Interface().(driver.Valuer); ok {
				//正常指针类型
				tmp, _ := val.Value()
				if uid == 0 {
					args[modname] = tmp
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = tmp
				}
				break
			} else {
				defOpt(v.Interface())
			}

		case reflect.Struct:
			if val, ok := v.Interface().(driver.Valuer); ok {
				tmp, _ := val.Value()
				if uid == 0 {
					args[modname] = tmp
				} else {
					args[utilmd.GetFieldKey(uid, modname)] = tmp
				}
				break
			}
			defOpt(v.Int())
		case reflect.Int32:
			//有可能是枚举
			defOpt(v.Int())
		default:
			defOpt(v.Interface())
		}
	}
	return rd.HMSet(rd.Ctx, utilmd.Key(table), args).Err()
}

// RowsSingleDels 删除多行数据，根据指定表和字段列表删除Redis中的数据
func (utilmd *DataUtil) RowsSingleDels(table string, uid uint64, li map[string]reflect.Value) {
	rd := utilmd.getRedisF()
	args := make([]string, 0, len(li)*2)
	for modname := range li {
		if uid == 0 {
			args = append(args, modname)
		} else {
			args = append(args, utilmd.GetFieldKey(uid, modname))
		}
	}
	rd.HDel(rd.Ctx, utilmd.Key(table), args...)
}

// GetKeySplitToFieldName 获取字段名字和主键ID，将键名拆分为ID和字段名
func (utilmd *DataUtil) GetKeySplitToFieldName(key string) (id uint64, field string) {
	var err error
	if tmpli := strings.Split(key, ":"); len(tmpli) == 1 {
		if id, err = common.NewString(tmpli[0]).ToUint64(); err != nil {
			//如果不能转成ID应该就是单行多字段
			field = tmpli[0]
		}

	} else if len(tmpli) > 1 {
		id = common.NewString(tmpli[0]).ToUint64V()
		field = tmpli[1]
	}
	return
}

// GetFieldKey 获取字段Key，根据ID和字段名生成
func (utilmd *DataUtil) GetFieldKey(id uint64, modname string) (key string) {
	return fmt.Sprintf("%d:%s", id, modname)
}
